<?php
/**
 * 编译器
 *
 * @package util.compile
 * @since 0.2
 */
class ICompiler {
	private static $config;
	private static $currentProcess;
	
	public static function init(array $config) {
		self::$config = $config;
	}
	
	public static function getCompileDir() {
		return self::$config["compile-dir"];
	}
	
	public static function getTtl() {
		return self::$config["ttl"];
	}
	
	public static function debug($message) {
		if (self::$config["debug"]) {
			echo date("Y-m-d H:i:s") . " - " . $message . "<br/>\n";
		}
	}
	
	public static function putProcess(ICompilableProcess &$process) {
		self::$currentProcess = &$process;
	}
	
	public static function &getCurrentProcess() {
		return self::$currentProcess;
	}
}

/**
 * 可编译过程
 *
 * @package util.compile
 * @since 0.2
 */
class ICompilableProcess {
	private $content;
	private $file;
	private $isCompiled = false;
	private $initialized = false;
	private $files = array();
	
	public function __construct($id) {
		ICompiler::putProcess($this);
		$this->file = ICompiler::getCompileDir() . "/" . urlencode($id) . ".compile.php";
		ICompiler::debug("file before parsed :{$this->file}");
		ICompiler::debug("file:{$this->file}");
		if (!is_file($this->file) || time() - filemtime($this->file) > ICompiler::getTtl()) {
			$this->isCompiled = false;
			$this->content = "<?php\n";
		}
		else {
			$this->isCompiled = true;
		}
	}
	
	public function init() {
		if (!$this->initialized) {
			if ($this->isCompiled) {
				$this->content = file_get_contents($this->file);
			}
			$this->initialized = true;
		}
	}
	
	public function isCompiled() {
		return $this->isCompiled;
	}
	
	public function getContent() {
		$this->init();
		return $this->content;
	}
	
	public function commit() {
		$this->init();
		ICompiler::debug("commit");
				
		//更新files列表
		$files = "\$COMPILED_FILES=" . var_export($this->files, true) . ";";
		$this->merge("COMPILED_FILES", $files);
		
		$fp = fopen($this->file, "w+");
		fwrite($fp, ltrim($this->content));
		fclose($fp);
	}
	
	public function merge($resName, $resContent) {
		$this->init();
		$start = $this->getStartLabel($resName);
		$end = $this->getEndLabel($resName);
		$qStart = preg_quote($start, "/");
		$qEnd = preg_quote($end, "/");
		if (preg_match("/{$qStart}(.+){$qEnd}/sU", $this->content)) {
			$this->content = preg_replace_callback("/({$qStart})(.+)({$qEnd})/sU", create_function("\$match", "global \$start,\$end,\$resContent;return \$start . \$resContent . \$end;"), $this->content);
		}
		else {
			$this->content .= "\n\n" . $start . $resContent . $end; 
		}
	}
	
	public function mergeFile($resName, $resFile) {
		$this->init();
		ICompiler::debug("merge file '{$resFile}'");
		$contents = file_get_contents($resFile);
		$contents = trim(preg_replace("/^<\\?(php)?|\\?>$/", "", trim($contents)));
		
		//去除注释
		$contents = preg_replace_callback("|/\\*\\*\\s*\n.+\\*/|sU", create_function("\$match", "return null;"), $contents);
		$contents = preg_replace_callback("/\\s*((private|public|protected) function)/sU", create_function("\$match", "return '\n' . \$match[1];"), $contents);
		$contents = preg_replace_callback("/\n\t+/s", create_function("\$match", "return '\n';"), $contents);
		
		//修改数组
		$this->files[$resName] = array(
			"file" => $resFile,
			"time" => time()
		);
		
		$this->merge($resName, $contents);
	}
	
	/**
	 * 加载编译文件
	 *
	 * @since 0.2
	 */
	public function load() {
		if ($this->isCompiled) {
			ICompiler::debug("load success");
			require($this->file);
			if (isset($COMPILED_FILES)) {
				$this->files = $COMPILED_FILES;

				//检查文件是否更新
				$renew = false;
				foreach ($this->files as $resName => $info) {
					if (filemtime($info["file"]) > $info["time"]) {
						$this->mergeFile($resName, $info["file"]);
						$renew = true;
					}
				}
				if ($renew) {
					$this->commit();
				}
			}
		}
	}
	
	private static function isAbsolutePath($path) {
		$path = trim($path);
		if (preg_match("/^\\//", $path) || preg_match("/^\\\\/", $path) || preg_match("/^\\w:\\\\/", $path)) {
			return true;
		}
		return false;
	}
	
	private function getStartLabel($source) {
		return "//{{{resource://" . $source . "\n";
	}
	
	private function getEndLabel($source) {
		return "\n" . "//resource://{$source}}}}";
	}
}

?>